/*
 *
 * Copyright (c) 1994, 2004, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * -Redistribution of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * Redistribution in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in
 * the documentation and/or other materials provided with the
 * distribution.
 *
 * Neither the name of Oracle nor the names of
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any
 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
 * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT
 * OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT,
 * SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF
 * THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS
 * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or
 * intended for use in the design, construction, operation or
 * maintenance of any nuclear facility.
 */
package server;

import java.io.*;

import javax.net.ssl.*;
import javax.security.cert.X509Certificate;

import client.Authenticator;
import client.Transceiver;

/* ClassFileServer.java -- a simple file server that can server
 * Http get request in both clear and secure channel
 *
 * The ClassFileServer implements a ClassServer that
 * reads files from the file system. See the
 * doc for the "Main" method for how to run this
 * server.
 */

public class ServerTransceiver extends Transceiver implements Runnable {

	private ServerAuthenticator connection;
	private String docroot;
	private static final int DefaultServerPort = 7777;
	private static final String DEFAULT_DOCUMENT_ROOT = "server";

	/**
	 * Constructs a ClassFileServer.
	 * 
	 * @param path
	 *            the path where the server locates files
	 */
	public ServerTransceiver(ServerAuthenticator connection, String docroot) {
		super(docroot);
		this.connection = connection;
		newListener();
	}

	/**
	 * Main method to create the class server that reads files. This takes two
	 * command line arguments, the port on which the server accepts requests and
	 * the root of the path. To start up the server: <br>
	 * <br>
	 * 
	 * <code>   java ClassFileServer <port> <path>
	 * </code><br>
	 * <br>
	 * 
	 * <code>   new ClassFileServer(port, docroot);
	 * </code>
	 */
	public static void main(String args[]) {
		System.out.println("server started");

		ServerAuthenticator connection = new ServerAuthenticator("server",
				"keystore/serverkeystore", "keystore/servertruststore",
				DefaultServerPort);
		new ServerTransceiver(connection, DEFAULT_DOCUMENT_ROOT);
	}

	/**
	 * Create a new thread to listen.
	 */
	private void newListener() {
		(new Thread(this)).start();
	}

	/**
	 * The "listen" thread that accepts a connection to the server, parses the
	 * header to obtain the file name and sends back the bytes for the file (or
	 * error if the file is not found or the response was malformed).
	 */
	public void run() {
		SSLSocket socket;

		// accept a connection

		socket = connection.validateIncomingConnection();
		if (socket == null)
			return;

		String subject = "";
		try {
			SSLSession session = socket.getSession();
			X509Certificate cert;
			cert = (X509Certificate) session.getPeerCertificateChain()[0];
			subject = cert.getSubjectDN().getName();

			//
			// Remove this in final program, the logging can get it's
			// information from
			// this.
			//
			// System.out.println("socket information");
			// System.out.println(socket);
			// System.out.println();
			// System.out.println("connecting client's certificate informaton");
			// System.out.println (subject);
			// System.out.println();
		} catch (SSLPeerUnverifiedException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		String userID = Authenticator.getUserID(subject);
		String groupID = Authenticator.getGroupID(subject);

		// create a new thread to accept the next connection
		newListener();

		try {
			OutputStream rawOut = socket.getOutputStream();

			PrintWriter out = new PrintWriter(new BufferedWriter(
					new OutputStreamWriter(rawOut)));
			try {
				// get path to class file from header
				BufferedReader in = new BufferedReader(new InputStreamReader(
						socket.getInputStream()));

				// Handle get and store command here
				String request = in.readLine();
				System.out.println(userID + ", " + groupID + ": " + request);
				Logger.auditAction(userID, "request: " + request);
				
				// get the arguments from the request
				String arg = request.substring(request.indexOf(' ') + 1);
				
				int endIndexOfPath = arg.indexOf(' ');
				String path = arg;
				String nurseID = "-1";
				if (endIndexOfPath != -1) {
					path = arg.substring(0, endIndexOfPath);
					nurseID = arg.substring(endIndexOfPath + 1);
				}
				String[] argParts = path.split("\\\\");
				path = path + RightsHandler.JOURNAL_FILE_EXTENSION;

				
				if (request.startsWith("get ") && argParts.length == 2) {
					if (new File(DEFAULT_DOCUMENT_ROOT + File.separator + path).exists() && RightsHandler.hasRightsToRead(userID, groupID,
							argParts[0], argParts[1])) {
						sendFromFile(out, rawOut, path);
						Logger.auditAction(userID, path + " sent to user");
					} else {
						System.out.println("permission denied");
						Logger.auditAction(userID, "reading " + path + " denied");
						out.println("permission denied");
						out.flush();
					}

				} else if (request.startsWith("store ") && argParts.length == 2) {
					File file = new File(DEFAULT_DOCUMENT_ROOT + File.separator + path);
					if (RightsHandler.hasRightsToWrite(userID, groupID,
							argParts[0], argParts[1])) {
						if (file.exists()) {
								receiveFile(in, path);
								Logger.auditAction(userID, path + " stored successfully");
								out.println("journal stored successfully");
								out.flush();
						} else {
							Logger.auditAction(userID, "tried to store a non-existing journal");
							System.out.println("journal does not exist");
							out.println("journal does not exist");
							out.flush();
						}
					} else {
						System.out.println("permission denied");
						Logger.auditAction(userID, "writing to " + path + " denied");
						out.println("permission denied");
						out.flush();
					}

				} else if (request.startsWith("create ") && argParts.length == 2 && !nurseID.equals("-1")) {

					if(RightsHandler.hasRightsToCreate(userID)) {
						File folder = new File(DEFAULT_DOCUMENT_ROOT + File.separator
								+ argParts[0]);
						if (!folder.exists()) {
							folder.mkdir();
						}
						File journal = new File(DEFAULT_DOCUMENT_ROOT + File.separator
								+ path);
						if (!journal.exists()) {
							receiveFile(in, path);
							RightsHandler.createRightsFile(argParts[0] + "\\" + argParts[1] + RightsHandler.RIGHTS_FILE_EXTENSION, groupID, userID, nurseID);
							System.out.println("journal created successfully");
							Logger.auditAction(userID, path + " created successfully");
							out.println("journal created successfully");
							out.flush();
						} else {
							System.out.println("journal already exits");
							Logger.auditAction(userID, "tried to create an already existing journal");
							out.println("journal already exits");
							out.flush();
						}
							
					} else {
						System.out.println("permission denied");
						Logger.auditAction(userID, "creating " + path + " denied");
						out.println("permission denied");
						out.flush();
					}

				} else if (request.startsWith("delete ") && argParts.length == 2) {
					if (RightsHandler.hasRightsToDelete(userID)) {
						File journal = new File(DEFAULT_DOCUMENT_ROOT + File.separator
								+ path);
						File rights = new File(DEFAULT_DOCUMENT_ROOT + File.separator
								+ arg + RightsHandler.RIGHTS_FILE_EXTENSION);
						File folder = new File(DEFAULT_DOCUMENT_ROOT + File.separator + argParts[0]);
						if (journal.exists()) {
							journal.delete();
							rights.delete();
							if (folder.list().length < 1) {
								folder.delete();
							}
							System.out.println("journal deleted successfully");
							Logger.auditAction(userID, "deleted " + path);
							out.println("journal deleted successfully");
							out.flush();
						} else {
							Logger.auditAction(userID, "tried to delete a non-existing file");
							System.out.println("journal does not exist");
							out.println("journal does not exist");
							out.flush();
						}
					} else {
						System.out.println("permission denied");
						Logger.auditAction(userID, "deleting " + path + " denied");
						out.println("permission denied");
						out.flush();
					}

				} else if (request.startsWith("list ") && argParts.length == 1) {
					if (arg.length() != 0) {
						String[] readable = RightsHandler.getReadableFiles(userID, groupID, arg);
						for(int i = 0; i < readable.length; i++){
							out.println(readable[i]);
						}
						out.flush();
						Logger.auditAction(userID, "listed " + argParts[0]);
						System.out.println("journals listed");
					} else {
						System.out.println("no patient specified");
						Logger.auditAction(userID, "tried to list all patients");
						out.println("no patient specified");
						out.flush();
					}
				} else {
					out.println("unrecognized command");
					out.flush();
				}

			} catch (Exception e) {
				e.printStackTrace();
				out.println(e.getMessage());
				out.flush();
			}

		} catch (IOException ex) {
			// eat exception (could log error to log file, but
			// write out to stdout for now).
			System.out.println("error writing response: " + ex.getMessage());
			ex.printStackTrace();

		} finally {
			try {
				socket.close();
			} catch (IOException e) {
			}
		}
	}

	private void receiveFile(BufferedReader in, String path) throws IOException {
		String inputLine;
		while ((inputLine = in.readLine()) != null) {
			if (inputLine.startsWith(BEGINNING_OF_FILE)) {
				writeToFile(in, path);
				break;
			} else {
				System.out.println(inputLine);
			}
		}
	}
}